package cn.apthink.controller.system;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.core.handle.Context;

import cn.apthink.controller.BaseResult;
import cn.apthink.controller.DataResult;
import cn.apthink.controller.ListResult;
import cn.apthink.model.ActionLog;
import cn.apthink.model.VMenu;
import cn.apthink.model.VRole;
import cn.apthink.model.VUser;
import cn.apthink.mybatisplus.entity.UserEntity;
import cn.apthink.service.AuthService;
import cn.apthink.solon.CallTimer;
import cn.apthink.solon.Logging;
import cn.apthink.utility.DateUtil;
import cn.apthink.utility.ObjectUtil;
import cn.apthink.utility.Util;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.TokenSign;
import cn.dev33.satoken.stp.StpUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;

/**
 * 系统安全
 * @author David唐
 * @createdate 2024年1月18日
 */
@Slf4j
@Controller
@Api(value="安全控制器")
@Mapping("/api")
public class AuthController {
	@Inject
	AuthService authService;

	@Logging
	@SaIgnore
	@Mapping("/login")
	@ApiOperation(value="登录", notes="同账户登录时相同设备互斥")
	public BaseResult login(
			@ApiParam(name = "account", value = "账户名", required = true) String account,
			@ApiParam(name = "password", value = "密码", required = true) String password,
			@ApiParam(name = "device", value = "设备", required = false) String device,
			Context ctx) {
		if (StpUtil.isLogin()) StpUtil.logout();
		// 1.开始计时
		long start = System.currentTimeMillis();
		String startTime = DateUtil.now();
		UserEntity user = authService.getUserByAccount(account);
		if (!Objects.isNull(user)) {
			String pass = Objects.isNull(user.getPassword()) ? "" : user.getPassword().replace("{noop}", "");
			if (pass.equals(password)) {
				String d;
				if (Util.isEmpty(device)) {
					String userAgent = ctx.header("User-Agent");
					d = Util.getBrowseType(userAgent);
				} else {
					d = device;
				}
				StpUtil.login(user.getId(), d);
				//AccountSession记录登录账户信息
				StpUtil.getSession().set("userentity", user);
				//TokenSession记录（ActionLogIntercepted拦截器忽略）
				// 2.获得接口响应时长
				long timespan = System.currentTimeMillis() - start;
				ActionLog log = new ActionLog();
				log.setDevice(d);
				log.setIpaddress(ctx.realIp());
				log.setMethod("login");
				log.setController(this.getClass().getName());
				log.setStarttime(startTime);
				log.setEndtime(DateUtil.now());
				log.setConsumedtime(timespan);
				StpUtil.getTokenSession().set("latestaction", log);
				// logService.saveActionLog(log);
				return DataResult.success("登录成功", StpUtil.getTokenValue());
			}
		}
		return BaseResult.error(10, "登录失败");
	}

	@Mapping("/logout")
	@ApiOperation("退出")
	public BaseResult logout() {
		StpUtil.logout();
		return BaseResult.success("退出成功");
	}

	@SaIgnore
	@Mapping("/isLogin")
	@ApiOperation("是否登录")
	public BaseResult isLogin() {
		return BaseResult.success("当前会话是否登录：" + StpUtil.isLogin());
	}

	@CallTimer
	@Mapping("/current")
	@ApiOperation("获取登录账户信息")
	public BaseResult current() {
		String uid = (String) StpUtil.getLoginId();
		UserEntity user = authService.getUserByUid(uid);
		if (Objects.isNull(user)) {
			return DataResult.error(99,  "账户数据不存在", "user");
		}
		log.info(user.toString());
		VUser vuser = ObjectUtil.clone(user, VUser.class);
		DataResult<VUser> result = new DataResult<>();
		result.setData(vuser);
		Set<VRole> roles = authService.getRolesByUserUid(user.getId());
		vuser.setRoles(roles);
		Set<VMenu> menus = authService.getMenusByRoles(roles);
		vuser.setMenus(menus);
		Set<String> permissions = authService.getPermissionByRoles(roles);
		vuser.setPermissions(permissions);
		return result;
	}
	
	@Mapping("/online")
	@ApiOperation("获取所有在线会话信息")
	public BaseResult online() {
		// 获取所有已登录的会话id
		List<String> sessionIdList = StpUtil.searchSessionId("", 0, -1, false);
		List<VOnine> result = new ArrayList<VOnine>();
		for (String sessionId : sessionIdList) {
			VOnine onlineUser = new VOnine();
		    // 根据会话id，查询对应的 SaSession 对象，此处一个 SaSession 对象即代表一个登录的账号 
		    SaSession session = StpUtil.getSessionBySessionId(sessionId);
		    UserEntity user = (UserEntity) session.get("userentity");
		    if (!Util.isEmpty(user)) {
		    	ObjectUtil.copy(user, onlineUser);
		    	onlineUser.setUserid(user.getId());
		    }
		    // 查询这个账号都在哪些设备登录了，依据上面的示例，账号A 的 tokenSign 数量是 3，账号B 的 tokenSign 数量是 2 
		    List<TokenSign> tokenSignList = session.getTokenSignList();
		    log.debug("会话id：" + sessionId + "，共在 " + tokenSignList.size() + " 设备登录");
		    for (TokenSign ts : tokenSignList ) {
		    	SaSession t = StpUtil.getTokenSessionByToken(ts.getValue());
		    	ActionLog log = t.getModel("latestaction", ActionLog.class);
		    	VOnlineDetail od = new VOnlineDetail();
		    	ObjectUtil.copy(log, od);
		    	od.setToken(t.getId());
		    	od.setCreatetime(DateUtil.timestamToDatetimeStr(t.getCreateTime()));
		    	onlineUser.addDetail(od);
		    }
		    result.add(onlineUser);
		}
		return ListResult.success(result);
	}	

}
